﻿using System;
using System.Collections.Generic;

namespace TheSnake
{
    public class Engine
    {
        public IRenderer renderer;
        public IUserInterface userInterface;
        public List<GameObject> allObjects;
        List<MovingObject> movingObjects;
        List<StaticObject> staticObjects;
        Snake playerSnake;

        public short Sleeptime { get; set; } //sleeptime property

        public Engine(IRenderer renderer, IUserInterface userInterface)
            : this(renderer, userInterface, 500)
        {

        }
        //additional constructor
        public Engine(IRenderer renderer, IUserInterface userInterface, short sleeptime)
        {
            this.renderer = renderer;
            this.userInterface = userInterface;
            this.allObjects = new List<GameObject>();
            this.movingObjects = new List<MovingObject>();
            this.staticObjects = new List<StaticObject>();
            if (sleeptime > 0)
            {
                this.Sleeptime = sleeptime;
            }
            else
            {
                throw new ArgumentOutOfRangeException("Sleep time should be positive integer number!");
            }
        }

        private void AddStaticObject(StaticObject obj)
        {
            this.staticObjects.Add(obj);
            this.allObjects.Add(obj);
        }

        private void AddMovingObject(MovingObject obj)
        {
            this.movingObjects.Add(obj);
            this.allObjects.Add(obj);
        }

        public virtual void AddObject(GameObject obj)
        {
            if (obj is MovingObject)
            {
                if (obj is Snake)
                {
                    AddSnake(obj);
                }
                else
                {
                    this.AddMovingObject(obj as MovingObject);
                }
            }
            else
            {
                this.AddStaticObject((StaticObject)obj);
            }
        }

        private void AddSnake(GameObject obj)
        {
            this.playerSnake = obj as Snake;
            this.AddMovingObject(obj as MovingObject);
        }

        public virtual void MovePlayerSnakeLeft()
        {
            this.playerSnake.MoveLeft();
        }

        public virtual void MovePlayerSnakeRight()
        {
            this.playerSnake.MoveRight();
        }

        public virtual void MovePlayerSnakeUp()
        {
            this.playerSnake.MoveUp();
        }

        public virtual void MovePlayerSnakeDown()
        {
            this.playerSnake.MoveDown();
        }

        public virtual void Run()
        {
            while (true)
            {
                this.renderer.RenderAll();

                System.Threading.Thread.Sleep(this.Sleeptime);//instead of hard-coded

                this.userInterface.ProcessInput();

                this.renderer.ClearQueue();

                foreach (var obj in this.allObjects)
                {
                    obj.Update();

                    if (obj is Snake)
                    {
                        Snake snakeObj = (Snake)obj;
                        this.renderer.EnqueueForRendering((IRenderable)snakeObj.Head);
                        foreach (var snakeTailPart in snakeObj.Tail)
                        {
                            this.renderer.EnqueueForRendering((IRenderable)snakeTailPart);
                        }
                        this.renderer.EnqueueForRendering((IRenderable)snakeObj.SnakeStatus);
                    }
                    else
                    {
                        this.renderer.EnqueueForRendering((IRenderable)obj);
                    }
                }

                try
                {
                    CollisionDispatcher.HandleCollisions(this.movingObjects, this.staticObjects);
                    CollisionDispatcher.HandleCollisions(this.movingObjects, this.movingObjects);
                }
                catch (CollisionException e)
                {
                    Food foodObj = (Food)e.Obj;
                    foodObj.ChangePosition(new Random().Next(500, 200000));
                }

                try
                {
                    CollisionDispatcher.HandleCollisions(this.staticObjects, this.staticObjects);
                }
                catch (CollisionException e)
                {
                    if (e.Obj != null)
                    {
                        Bonus bonusObj = (Bonus)e.Obj;
                        bonusObj.ChangePosition(new Random().Next(500, 200000));
                    }
                }

                List<GameObject> producedObjects = new List<GameObject>();

                //foreach (var obj in this.allObjects)
                //{
                //    producedObjects.AddRange(obj.ProduceObjects());
                //}

                //this.allObjects.RemoveAll(obj => obj.IsDestroyed);
                //this.movingObjects.RemoveAll(obj => obj.IsDestroyed);
                //this.staticObjects.RemoveAll(obj => obj.IsDestroyed);

                foreach (var obj in producedObjects)
                {
                    this.AddObject(obj);
                }
            }
        }
    }
}
